package com.szy.redstonemcu;

import com.szy.assembly.*;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ITickable;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class McuBaseEntity extends TileEntity implements ITickable, IInventory , IOChangedNotifier {

    private final int NUMBER_OF_SLOTS=1;
    private ItemStack[] itemStacks;
    public ArrayList<Peripheral> peripherals=new ArrayList<>();
    public CPUSimulator simulator=new CPUSimulator(Program.getDefaultProgram(),1,peripherals);

    //public EditScreen editScreen=new EditScreen();

    public String codeText="";
    public int[] ioPower=new int[16];
    public McuBaseEntity(){
        super();
        itemStacks = new ItemStack[NUMBER_OF_SLOTS];
        Arrays.fill(itemStacks, ItemStack.EMPTY);

        //peripherals.add(new GPIO(0x08000000).setIoChangedNotifier(this));
        //RedstoneMCU.logger.info("new entity");
    }


    public ArrayList<Peripheral> getPeripherals(){
        return peripherals;
    }

    @Override
    public void readFromNBT(NBTTagCompound parentNBTTagCompound) {
        super.readFromNBT(parentNBTTagCompound); // The super call is required to save and load the tiles location
        this.codeText=parentNBTTagCompound.getString("codeText");

        byte[] simulatorData=parentNBTTagCompound.getByteArray("simulatorData");
        ByteArrayInputStream bis=new ByteArrayInputStream(simulatorData);
        ObjectInput in=null;
        try{
            in=new ObjectInputStream(bis);
            simulator=(CPUSimulator) in.readObject();
            this.peripherals=simulator.getPeripherals();
            for(Peripheral peripheral:peripherals){
                peripheral.setIoChangedNotifier(this);
                peripheral.setInterruptRequester(simulator);
            }
        }catch (IOException e){
            e.printStackTrace();
        }catch (ClassNotFoundException e){
            e.printStackTrace();
            simulator=new CPUSimulator(Program.getDefaultProgram(),256,peripherals);
        }
        ioPower=parentNBTTagCompound.getIntArray("ioPower");
        if(ioPower.length == 0){
            ioPower=new int[10];
        }


        final byte NBT_TYPE_COMPOUND = 10;       // See NBTBase.createNewByType() for a listing
        NBTTagList dataForAllSlots = parentNBTTagCompound.getTagList("Items", NBT_TYPE_COMPOUND);

        Arrays.fill(itemStacks, ItemStack.EMPTY);           // set all slots to empty EMPTY_ITEM
        for (int i = 0; i < dataForAllSlots.tagCount(); ++i) {
            NBTTagCompound dataForOneSlot = dataForAllSlots.getCompoundTagAt(i);
            int slotIndex = dataForOneSlot.getByte("Slot") & 255;

            if (slotIndex >= 0 && slotIndex < this.itemStacks.length) {
                this.itemStacks[slotIndex] = new ItemStack(dataForOneSlot);
            }
        }
//        super.readFromNBT(tag);
//        this.progress = tag.getInteger("Progress");
//        this.fuel = tag.getInteger("Fuel");
    }

    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound parentNBTTagCompound) {
        super.writeToNBT(parentNBTTagCompound); // The super call is required to save and load the tileEntity's location
        parentNBTTagCompound.setString("codeText",codeText);

        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutput out=null;
        try{
            out=new ObjectOutputStream(bos);
            out.writeObject(simulator);
            out.flush();
            byte[] simulatorData=bos.toByteArray();
            //parentNBTTagCompound.setInteger("simulatorSize",simulatorData.length);
            parentNBTTagCompound.setByteArray("simulatorData",simulatorData);
        }catch (IOException e){
            e.printStackTrace();
        }
        parentNBTTagCompound.setIntArray("ioPower",ioPower);


        // to use an analogy with Java, this code generates an array of hashmaps
        // The itemStack in each slot is converted to an NBTTagCompound, which is effectively a hashmap of key->value pairs such
        //   as slot=1, id=2353, count=1, etc
        // Each of these NBTTagCompound are then inserted into NBTTagList, which is similar to an array.
        NBTTagList dataForAllSlots = new NBTTagList();
        for (int i = 0; i < this.itemStacks.length; ++i) {
            if (!this.itemStacks[i].isEmpty())	{ //isEmpty()
                NBTTagCompound dataForThisSlot = new NBTTagCompound();
                dataForThisSlot.setByte("Slot", (byte) i);
                this.itemStacks[i].writeToNBT(dataForThisSlot);
                dataForAllSlots.appendTag(dataForThisSlot);
            }
        }
        // the array of hashmaps is then inserted into the parent hashmap for the container
        parentNBTTagCompound.setTag("Items", dataForAllSlots);
        // return the NBT Tag Compound
        return parentNBTTagCompound;
//        tag.setInteger("Progress", this.progress);
//        tag.setInteger("Fuel", this.fuel);
//        return super.writeToNBT(tag);
    }

    @Override
    public int getSizeInventory() {
        return itemStacks.length;
    }
    // returns true if all of the slots in the inventory are empty
    @Override
    public boolean isEmpty() {
        for (ItemStack itemstack : itemStacks) {
            if (!itemstack.isEmpty()) {  // isEmpty()
                return false;
            }
        }
        return true;
    }
    // Gets the stack in the given slot
    @Override
    public ItemStack getStackInSlot(int slotIndex) {
        return itemStacks[slotIndex];
    }
    @Override
    public ItemStack decrStackSize(int slotIndex, int count) {
        ItemStack itemStackInSlot = getStackInSlot(slotIndex);
        if (itemStackInSlot.isEmpty()) return ItemStack.EMPTY;  // isEmpt();   EMPTY_ITEM

        ItemStack itemStackRemoved;
        if (itemStackInSlot.getCount() <= count) {  // getStackSize()
            itemStackRemoved = itemStackInSlot;
            setInventorySlotContents(slotIndex, ItemStack.EMPTY);   // EMPTY_ITEM
        } else {
            itemStackRemoved = itemStackInSlot.splitStack(count);
            if (itemStackInSlot.getCount() == 0) { // getStackSize
                setInventorySlotContents(slotIndex, ItemStack.EMPTY);   // EMPTY_ITEM
            }
        }
        markDirty();
        return itemStackRemoved;
    }
    @Override
    public void setInventorySlotContents(int slotIndex, ItemStack itemstack) {
        itemStacks[slotIndex] = itemstack;
        if (itemstack.isEmpty() && itemstack.getCount() > getInventoryStackLimit()) { //  isEmpty(); getStackSize()
            itemstack.setCount(getInventoryStackLimit());  //setStackSize
        }
        markDirty();
    }
    @Override
    public int getInventoryStackLimit() {
        return 1;
    }

    @Override
    public boolean isUsableByPlayer(EntityPlayer player) {
        if (this.world.getTileEntity(this.pos) != this) return false;
        final double X_CENTRE_OFFSET = 0.5;
        final double Y_CENTRE_OFFSET = 0.5;
        final double Z_CENTRE_OFFSET = 0.5;
        final double MAXIMUM_DISTANCE_SQ = 8.0 * 8.0;
        return player.getDistanceSq(pos.getX() + X_CENTRE_OFFSET, pos.getY() + Y_CENTRE_OFFSET, pos.getZ() + Z_CENTRE_OFFSET) < MAXIMUM_DISTANCE_SQ;
    }

    @Override
    public boolean isItemValidForSlot(int slotIndex, ItemStack itemstack) {
        return true;
    }

    // set all slots to empty
    @Override
    public void clear() {
        Arrays.fill(itemStacks, ItemStack.EMPTY);  //empty item
    }

    // will add a key for this container to the lang file so we can name it in the GUI
    @Override
    public String getName() {
        return "container.mbe30_inventory_basic.name";
    }

    @Override
    public boolean hasCustomName() {
        return false;
    }

    // standard code to look up what the human-readable name is
    @Override
    public ITextComponent getDisplayName() {
        return this.hasCustomName() ? new TextComponentString(this.getName()) : new TextComponentTranslation(this.getName());
    }

    @Override
    public ItemStack removeStackFromSlot(int slotIndex) {
        ItemStack itemStack = getStackInSlot(slotIndex);
        if (!itemStack.isEmpty()) setInventorySlotContents(slotIndex, ItemStack.EMPTY);  //isEmpty(), EMPTY_ITEM
        return itemStack;
    }

    @Override
    public void openInventory(EntityPlayer player) {}

    @Override
    public void closeInventory(EntityPlayer player) {}

    @Override
    public int getField(int id) {
        return 0;
    }

    @Override
    public void setField(int id, int value) {}

    @Override
    public int getFieldCount() {
        return 0;
    }


    public int getIoPower(int ioNum){
        return ioPower[ioNum];
    }
    private int t;
    @Override
    public void update() {
        simulator.cycleTask();
//        if(!world.isRemote){
//            TileEntity entity=world.getTileEntity(pos);
//            if(entity instanceof RM8FAA_Entity){
//                RM8FAA_Entity te=(RM8FAA_Entity) entity;
//                te.ioChangedByHardware(0,world.getRedstonePower(pos.offset(EnumFacing.EAST),EnumFacing.EAST));
//                te.ioChangedByHardware(1,world.getRedstonePower(pos.offset(EnumFacing.SOUTH),EnumFacing.SOUTH));
//                te.ioChangedByHardware(2,world.getRedstonePower(pos.offset(EnumFacing.WEST),EnumFacing.WEST));
//                te.ioChangedByHardware(3,world.getRedstonePower(pos.offset(EnumFacing.NORTH),EnumFacing.NORTH));
//
//
//            }
//        }
//        t++;
//        if(t==20){
//            t=0;
//
            //this.world.notifyNeighborsOfStateChange(pos, this.world.getBlockState(pos).getBlock(), false);
//            //int p=this.world.getRedstonePower(pos.offset(EnumFacing.NORTH), EnumFacing.NORTH);
//            //RedstoneMCU.logger.info("north power="+p);
//        }
    }


    @Override
    public void ioChanged(int ioNumber, int value) {
        ioPower[ioNumber]=value;

        this.world.notifyNeighborsOfStateChange(pos, this.world.getBlockState(pos).getBlock(), false);

    }

    public void ioChangedByHardware(int ioNumber,int value){
        for(Peripheral peripheral:peripherals){
            peripheral.ioChangedByHardware(ioNumber,value);
        }
    }
}
